/**
 * 常用工具类
 */
package com.apache.attachment.mommon;

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.WritableRaster;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Iterator;

import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;

import com.apache.exception.BusinessException;
import com.apache.tools.LogUtil;

/**
 * 支持图片缩放、图片水印、文字水印、图片剪切 图片支持jpg、bmp、jpeg格式
 * @author ysliu 
 */
public final class ImageUtil {

	private static ImageUtil instance;

	public ImageUtil() {
	}

	public static ImageUtil getInstance() {
		if (null == instance) {
			instance = new ImageUtil();
		}
		return instance;
	}

	/**
	 * 图片水印
	 * 
	 * @param pressImg
	 *            水印图片
	 * @param targetImg
	 *            目标图片
	 * @param x
	 *            修正值 默认在中间
	 * @param y
	 *            修正值 默认在中间
	 * @param apache
	 *            透明度
	 * @throws IOException 
	 */
	public final void pressImage(String pressImg, String targetImg, int x, int y, float apache) throws IOException {
		File img = new File(targetImg);
		Image src = ImageIO.read(img);
		int wideth = src.getWidth(null);
		int height = src.getHeight(null);
		BufferedImage image = new BufferedImage(wideth, height, BufferedImage.TYPE_INT_RGB);
		Graphics2D g = image.createGraphics();
		g.drawImage(src, 0, 0, wideth, height, null);
		// 水印文件
		Image src_biao = ImageIO.read(new File(pressImg));
		int wideth_biao = src_biao.getWidth(null);
		int height_biao = src_biao.getHeight(null);
		//调整x、y的坐标
		if (x > (wideth - wideth_biao) / 2) {
			x = (wideth - wideth_biao) / 2;
		} else if (x < -((wideth - wideth_biao) / 2)) {
			x = -(wideth - wideth_biao) / 2;
		}
		if (y > (height - height_biao) / 2) {
			y = (height - height_biao) / 2;
		} else if (y < -((height - height_biao) / 2)) {
			y = -(height - height_biao) / 2;
		}
		g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, apache));
		g.drawImage(src_biao, (wideth - wideth_biao) / 2 + x, (height - height_biao) / 2 + y, wideth_biao, height_biao,
				null);
		// 水印文件结束
		g.dispose();
		ImageIO.write((BufferedImage) image, "jpg", img);
	}

	/**
	 * 文字水印
	 * 
	 * @param pressText
	 *            水印文字
	 * @param targetImg
	 *            目标图片
	 * @param fontName
	 *            字体名称
	 * @param fontStyle
	 *            字体样式
	 * @param color
	 *            字体颜色
	 * @param fontSize
	 *            字体大小
	 * @param x
	 *            修正值
	 * @param y
	 *            修正值
	 * @param apache
	 *            透明度
	 */
	public void pressText(String pressText, String targetImg, String fontName, int fontStyle, Color color,
			int fontSize, int x, int y, float apache) {
		try {
			File img = new File(targetImg);
			Image src = ImageIO.read(img);
			int width = src.getWidth(null);
			int height = src.getHeight(null);
			BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
			Graphics2D g = image.createGraphics();
			g.drawImage(src, 0, 0, width, height, null);
			g.setColor(color);
			g.setFont(new Font(fontName, fontStyle, fontSize));
			//调整x、y的坐标
			if (x > (width - (getLength(pressText) * fontSize)) / 2) {
				x = (width - (getLength(pressText) * fontSize)) / 2;
			} else if (x < -((width - (getLength(pressText) * fontSize))) / 2) {
				x = -((width - (getLength(pressText) * fontSize))) / 2;
			}
			if (y > (height - fontSize) / 2) {
				y = (height - fontSize) / 2;
			} else if (y < -((height - fontSize) / 2)) {
				y = -((height - fontSize) / 2);
			}
			g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, apache));
			g.drawString(pressText, (width - (getLength(pressText) * fontSize)) / 2 + x, (height - fontSize) / 2 + y);
			g.dispose();
			ImageIO.write((BufferedImage) image, "jpg", img);
		} catch (Exception e) {
			LogUtil.error("" + e);
		}
	}

	/**
	 * 缩放
	 * 
	 * @param source
	 *            图片
	 * @param targetW
	 *            宽
	 * @param targetH
	 *            高
	 * @param scale
	 *            是否是等比例缩放，true是，false非
	 * @return
	 */
	private BufferedImage resizeImage(BufferedImage source, int targetW, int targetH, boolean scale) {
		// targetW，targetH分别表示目标长和宽  
		int type = source.getType();
		BufferedImage target = null;
		double sx = (double) targetW / source.getWidth();
		double sy = (double) targetH / source.getHeight();
		//这里想实现在targetW，targetH范围内实现等比缩放。如果不需要等比缩放  
		//则将下面的if else语句注释即可
		if (scale) {
			if (sx > sy) {
				targetH = (int) (sx * source.getHeight());
				sy = sx;
			} else {
				targetW = (int) (sy * source.getWidth());
				sx = sy;
			}
		}
		if (type == BufferedImage.TYPE_CUSTOM) { //handmade  //not pic 
			ColorModel cm = source.getColorModel();
			WritableRaster raster = cm.createCompatibleWritableRaster(targetW, targetH);
			boolean apachePremultiplied = cm.isAlphaPremultiplied();
			target = new BufferedImage(cm, raster, apachePremultiplied, null);
		} else {
			target = new BufferedImage(targetW, targetH, type);
		}
		Graphics2D g = target.createGraphics();
		//smoother than exlax:  
		g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); //old 
		//g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);    //没有上一个效果好 
		g.drawRenderedImage(source, AffineTransform.getScaleInstance(sx, sy));
		g.dispose();
		return target;
	}

	/**
	 * 按照给定的宽和高进行缩放
	 * 	<b>非等比例缩放</b>
	 * @param source
	 * @param targetW
	 * @param targetH
	 * @return
	 */
	private BufferedImage resizeImage(BufferedImage source, int targetW, int targetH) {
		return resizeImage(source, targetW, targetH, false);
	}

	/**
	 * 按宽度等比例缩放
	 * 
	 * @param source
	 * @param targetW
	 * @return
	 */
	private BufferedImage resizeImageW(BufferedImage source, int targetW) {
		return resizeImage(source, targetW, 0, true);
	}

	/**
	 * 按长度等比例缩放
	 * 
	 * @param source
	 * @param targetH
	 * @return
	 */
	private BufferedImage resizeImageH(BufferedImage source, int targetH) {
		return resizeImage(source, 0, targetH, true);
	}

	/**
	 * 缩放，支持等比例缩放
	 * @param filePath 图片路径
	 * @param width 宽
	 * @param hight 高
	 * @throws PicException 
	 */
	public void resize(String filePath, int width, int hight) throws BusinessException {
		try {
			BufferedImage srcImage;
			// String ex =
			// fromFileStr.substring(fromFileStr.indexOf("."),fromFileStr.length());
			String imgType = "JPEG";
			if (filePath.toLowerCase().endsWith(".png")) {
				imgType = "PNG";
			}
			File file = new File(filePath);
			srcImage = ImageIO.read(file);
			if (width > 0 && hight > 0) {
				srcImage = resizeImage(srcImage, width, hight);
			} else if (width > 0) {
				srcImage = resizeImageW(srcImage, width);
			} else if (hight > 0) {
				srcImage = resizeImageH(srcImage, hight);
			} else {
				return;
			}
			ImageIO.write(srcImage, imgType, file);
		} catch (Exception e) {
			LogUtil.error("" + e);
			throw new BusinessException("pic resize error at read or write!!");
		}
	}

	/**
	 * 根据指定的位置和大小对图片进行裁剪
	 * @param filepath
	 * 			图片路径
	 * @param x
	 * 			要截取图片的起始坐标x点
	 * @param y
	 * 			要截取图片的起始坐标y点
	 * @param width
	 * 			要截取图片的宽度
	 * @param height
	 * 			要截取图片的高度
	 * @throws IOException
	 * 			读写异常
	 */
	public void cut(String filepath, int x, int y, int width, int height) throws IOException {

		FileInputStream is = null;
		ImageInputStream iis = null;

		try {
			// 读取图片文件
			is = new FileInputStream(filepath);

			/*
			 * 返回包含所有当前已注册 ImageReader 的 Iterator，这些 ImageReader 声称能够解码指定格式。
			 * 参数：formatName - 包含非正式格式名称 . （例如 "jpeg" 或 "tiff"）等 。
			 */
			Iterator<ImageReader> it = ImageIO.getImageReadersByFormatName("jpg");
			ImageReader reader = it.next();
			// 获取图片流
			iis = ImageIO.createImageInputStream(is);

			/*
			 * <p>iis:读取源.true:只向前搜索 </p>.将它标记为 ‘只向前搜索’。
			 * 此设置意味着包含在输入源中的图像将只按顺序读取，可能允许 reader 避免缓存包含与以前已经读取的图像关联的数据的那些输入部分。
			 */
			reader.setInput(iis, true);

			/*
			 * 获取原始图片的大小
			 */
			int originWidth = reader.getWidth(0);
			int originHeight = reader.getHeight(0);

			/*
			 * <p>描述如何对流进行解码的类<p>.用于指定如何在输入时从 Java Image I/O
			 * 框架的上下文中的流转换一幅图像或一组图像。用于特定图像格式的插件 将从其 ImageReader 实现的
			 * getDefaultReadParam 方法中返回 ImageReadParam 的实例。
			 */
			ImageReadParam param = reader.getDefaultReadParam();

			/*
			 * 图片裁剪区域。Rectangle 指定了坐标空间中的一个区域，通过 Rectangle 对象
			 * 的左上顶点的坐标（x，y）、宽度和高度可以定义这个区域。 宽度和高度不能超过原始图片，否则只能以原图片大小截取。
			 */
			if (width >= originWidth) {
				width = originWidth;
			}
			//System.out.println(width);
			if (height >= originHeight) {
				height = originHeight;
			}
			//System.out.println(height);
			if (x >= width) {
				x = 0;
			}
			//System.out.println(x);
			if (y >= height) {
				y = 0;
			}
			//System.out.println(y);
			Rectangle rect = new Rectangle(x, y, width, height);

			// 提供一个 BufferedImage，将其用作解码像素数据的目标。
			param.setSourceRegion(rect);

			/*
			 * 使用所提供的 ImageReadParam 读取通过索引 imageIndex 指定的对象，并将 它作为一个完整的
			 * BufferedImage 返回。
			 */
			BufferedImage bi = reader.read(0, param);

			// 保存新图片
			ImageIO.write(bi, "jpg", new File(filepath));
		}

		finally {
			if (is != null)
				is.close();
			if (iis != null)
				iis.close();
		}

	}

	private int getLength(String text) {
		int length = 0;
		for (int i = 0; i < text.length(); i++) {
			if (new String(text.charAt(i) + "").getBytes().length > 1) {
				length += 2;
			} else {
				length += 1;
			}
		}
		return length / 2;
	}
}